package com.xinzhidi.web.equipment.service.impl;

import cn.hutool.core.thread.ThreadUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinzhidi.common.constant.AgreementConstants;
import com.xinzhidi.common.core.domain.model.LoginUser;
import com.xinzhidi.common.core.page.TableDataInfo;
import com.xinzhidi.common.exception.ServiceException;
import com.xinzhidi.common.utils.DateUtils;
import com.xinzhidi.common.utils.SecurityUtils;
import com.xinzhidi.common.utils.bean.BeanUtils;
import com.xinzhidi.system.service.ISysUserService;
import com.xinzhidi.web.build.service.IBuildUnitService;
import com.xinzhidi.web.build.service.ICBuildService;
import com.xinzhidi.web.equipment.domain.Equipment;
import com.xinzhidi.web.equipment.domain.EquipmentArchives;
import com.xinzhidi.web.equipment.domain.EquipmentArchivesRestore;
import com.xinzhidi.web.equipment.dto.*;
import com.xinzhidi.web.equipment.mapper.EquipmentArchivesMapper;
import com.xinzhidi.web.equipment.service.EquipmentService;
import com.xinzhidi.web.equipment.service.IEquipmentArchivesRestoreService;
import com.xinzhidi.web.equipment.service.IEquipmentArchivesService;
import com.xinzhidi.web.equipment.vo.EquipmentArchivesInfoVo;
import com.xinzhidi.web.equipment.vo.EquipmentArchivesListVo;
import com.xinzhidi.web.netty.entity.DeviceInstruction;
import com.xinzhidi.web.netty.service.AgreementSeahorseValveService;
import com.xinzhidi.web.netty.service.DeviceInstructionService;
import com.xinzhidi.web.plot.service.ICPlotService;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;


/**
 * 存档Service业务层处理
 *
 * @author cx
 * @date 2023-07-12
 */
@Service
public class EquipmentArchivesServiceImpl extends ServiceImpl<EquipmentArchivesMapper, EquipmentArchives> implements IEquipmentArchivesService {
    @Autowired
    private EquipmentArchivesMapper equipmentArchivesMapper;
    @Autowired
    private IEquipmentArchivesRestoreService archivesRestoreService;
    @Autowired
    private ICPlotService icPlotService;
    @Autowired
    private ICBuildService icBuildService;
    @Autowired
    private IBuildUnitService iBuildUnitService;
    @Autowired
    private ISysUserService iSysUserService;

    @Autowired
    private EquipmentService equipmentService;

    @Autowired
    private AgreementSeahorseValveService seahorseValveService;

    @Autowired
    private DeviceInstructionService deviceInstructionService;

    private static final String ERROR_MESSAGE = "当前数据选择错误，请重新选择";

    /**
     * 查询存档
     *
     * @param id 存档主键
     * @return 存档
     */
    @Override
    public EquipmentArchivesInfoVo selectEquipmentArchivesById(Long id) {
        EquipmentArchives equipmentArchives = equipmentArchivesMapper.selectById(id);
        EquipmentArchivesInfoVo infoVo = new EquipmentArchivesInfoVo();
        BeanUtils.copyProperties(equipmentArchives, infoVo);
        return infoVo;
    }

    /**
     * 查询存档列表
     *
     * @param dto 查询存档dto
     * @return 存档
     */
    @Override
    public TableDataInfo<EquipmentArchivesListVo> selectEquipmentArchivesList(EquipmentArchivesListDto dto) {
        List<EquipmentArchivesListVo> list = equipmentArchivesMapper.selectListNative(dto);
        if (CollectionUtils.isNotEmpty(list)) {
            list.forEach(item -> {
                String info = item.getPlotName();
                if (item.getBuildName() != null) {
                    info += "-" + item.getBuildName();
                }
                if (item.getUnitName() != null) {
                    info += "-" + item.getUnitName();
                }
                item.setInfo(info);
            });
        }

        return TableDataInfo.getDataTable(list);
    }

    /**
     * 新增存档
     *
     * @param dto 新增存档dto
     * @return 结果
     */
    @Override
    public int insertEquipmentArchives(EquipmentArchivesAddDto dto) {
        LoginUser loginUser = SecurityUtils.getLoginUser();

        EquipmentArchives equipmentArchives = new EquipmentArchives();
        BeanUtils.copyProperties(dto, equipmentArchives);
        equipmentArchives.setCreateBy(loginUser.getUserId().toString());
        equipmentArchives.setCreateTime(DateUtils.getNowDate());
        return equipmentArchivesMapper.insert(equipmentArchives);
    }

    /**
     * 新增存档
     *
     * @param dto 新增存档dto
     * @return 结果
     */
    @Override
    public int insertEquipmentArchivesList(EquipmentArchivesAddListDto dto) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        // 判断是否有跨小区存档
        List<Long> plotIds = dto.getArchiveSave().stream().map(EquipmentArchivesAddDto::getPlotId).distinct().collect(Collectors.toList());
        if (plotIds.size() > 1) {
            throw new ServiceException("不能跨小区存档！");
        }
        List<EquipmentArchives> equipmentArchivesList = dto.getArchiveSave().stream().map(item -> {
            if (item.getValveOpen() == null) {
                throw new ServiceException("开度不能为空");
            }
            EquipmentArchives equipmentArchives = new EquipmentArchives();
            BeanUtils.copyProperties(item, equipmentArchives);
            equipmentArchives.setCreateBy(loginUser.getUserId().toString());
            equipmentArchives.setCreateTime(DateUtils.getNowDate());
            return equipmentArchives;
        }).collect(Collectors.toList());
        saveBatch(equipmentArchivesList);
        return 1;
    }

    /**
     * 修改存档
     *
     * @param dto 修改存档dto
     * @return 结果
     */
    @Override
    public int updateEquipmentArchives(EquipmentArchivesUpdDto dto) {
        LoginUser loginUser = SecurityUtils.getLoginUser();

        EquipmentArchives equipmentArchives = new EquipmentArchives();
        BeanUtils.copyProperties(dto, equipmentArchives);
        equipmentArchives.setUpdateBy(loginUser.getUserId().toString());
        equipmentArchives.setUpdateTime(DateUtils.getNowDate());
        return equipmentArchivesMapper.updateById(equipmentArchives);
    }

    /**
     * 批量删除存档
     *
     * @param ids 需要删除的存档主键
     * @return 结果
     */
    @Override
    public int deleteEquipmentArchivesByIds(Long[] ids) {
        return equipmentArchivesMapper.deleteBatchIds(Arrays.asList(ids));
    }

    /**
     * 删除存档信息
     *
     * @param id 存档主键
     * @return 结果
     */
    @Override
    public int deleteEquipmentArchivesById(Long id) {
        return equipmentArchivesMapper.deleteById(id);
    }

    /**
     * 恢复存档
     *
     * @param dtoList 要恢复的存档数据
     * @return 结果
     */
    @Override
    public void recoverEquipmentArchives(List<recoverDto> dtoList) {

        //1：根据type的不同，获取不同维度的阀门设备
        //2：通过阀门设备组装对应的下发开度指令
        //3：保存指令至指令表
        LambdaQueryWrapper<Equipment> queryWrapper = Wrappers.lambdaQuery();
        if (CollectionUtils.isEmpty(dtoList)) {
            throw new ServiceException("恢复数据不能为空");
        }

        //检查有没有包含的数据。比如楼栋a，下面有个单元b，不能同事恢复楼栋a和单元b的数据
        this.checkContain(dtoList);
        List<Equipment> unitEquipmentList = Lists.newArrayList();
        List<Equipment> householdEquipmentList = Lists.newArrayList();
        List<DeviceInstruction> deviceInstructionList = Lists.newArrayList();
        EquipmentArchivesRestore restore;
        List<EquipmentArchivesRestore> restoreList = Lists.newArrayList();
        Long s = System.currentTimeMillis();
        for (recoverDto dto : dtoList) {
            String type = dto.getType();
            //阀门开度转为十六进制字符串。因为系数为0.1 ，所以需要乘以10
//            BigDecimal valveOpen = dto.getValveOpen().multiply(BigDecimal.valueOf(10));
            String valveOpenStr = dto.getValveOpen().toString();
            queryWrapper.eq(Equipment::getLotId, dto.getPlotId());
            queryWrapper.eq(Equipment::getProtocolType, AgreementConstants.AgreementType.UNIT_VALVE);
            if(type.equals(AgreementConstants.DataType.BUILD)){
                queryWrapper.eq(Equipment::getBuildId, dto.getBuildId());
            }else if(type.equals(AgreementConstants.DataType.BUILD_UNIT)){
                queryWrapper.eq(Equipment::getBuildId, dto.getBuildId());
                queryWrapper.eq(Equipment::getUnitId, dto.getUnitId());
            }
            unitEquipmentList = equipmentService.list(queryWrapper);

            queryWrapper.clear();
            queryWrapper.eq(Equipment::getLotId, dto.getPlotId())
                    .and(wrapper -> {
                        wrapper.eq(Equipment::getProtocolType, AgreementConstants.AgreementType.HOUSEHOLD_VALVE);
                        wrapper.or().eq(Equipment::getProtocolType, AgreementConstants.AgreementType.SEAHORSE_VALVE_METER);
                    });
            if(type.equals(AgreementConstants.DataType.BUILD)){
                queryWrapper.eq(Equipment::getBuildId, dto.getBuildId());
            }else if(type.equals(AgreementConstants.DataType.BUILD_UNIT)){
                queryWrapper.eq(Equipment::getBuildId, dto.getBuildId());
                queryWrapper.eq(Equipment::getUnitId, dto.getUnitId());
            }
            householdEquipmentList = equipmentService.list(queryWrapper);
            restore = new EquipmentArchivesRestore();
            restore.setPlotId(dto.getPlotId());
            restore.setUnitId(dto.getUnitId());
            restore.setBuildId(dto.getBuildId());
            restore.setType(dto.getType());
            restore.setValveOpen(dto.getValveOpen());
            restore.setStatus("成功");
            restore.setArchivesId(dto.getArchivesId());
            restore.setArchivesTime(DateUtils.parseDate(dto.getCreateTime()));
            restoreList.add(restore);
            //获取楼栋需要下发的阀门指令
            List<DeviceInstruction> unitInstructionList = deviceInstructionService.getDeviceInstructionList(
                    AgreementConstants.InstructBusinessType.ARCHIVE_RECOVERY, AgreementConstants.AgreementType.UNIT_VALVE,
                    AgreementConstants.InstructionType.SEND, "3", unitEquipmentList, valveOpenStr);

            Long ss = System.currentTimeMillis();
            // 多线程处理
            //初始化线程数量
            CountDownLatch countDownLatch = ThreadUtil.newCountDownLatch(householdEquipmentList.size());
            if (CollectionUtils.isNotEmpty(householdEquipmentList)) {
                for (Equipment equipment : householdEquipmentList) {
                    List<DeviceInstruction> finalInstructionList = deviceInstructionList;
                    List<Equipment> finalHouseholdEquipmentList = householdEquipmentList;
                    ThreadUtil.execute(() -> {

                        if (equipment.getProtocolType().equals(AgreementConstants.AgreementType.HOUSEHOLD_VALVE)) {
                            //获取住户需要下发的阀门指令
                            List<DeviceInstruction> householdInstructionList = deviceInstructionService.getDeviceInstructionList(
                                    AgreementConstants.InstructBusinessType.ARCHIVE_RECOVERY, AgreementConstants.AgreementType.HOUSEHOLD_VALVE,
                                    AgreementConstants.InstructionType.SEND, "3", finalHouseholdEquipmentList, valveOpenStr);
                            deviceInstructionList.addAll(householdInstructionList);
                        } else {
                            DeviceInstruction d = seahorseValveService.getWriteByModel(equipment.getDtuId(), equipment.getAddress(), "01", dto.getValveOpen().toString());
                            deviceInstructionList.add(d);
                        }
                        //调用线程计数器-1
                        countDownLatch.countDown();
                    });
                    if (!org.springframework.util.CollectionUtils.isEmpty(finalInstructionList)) {
                        deviceInstructionList.addAll(finalInstructionList);
                    }

                }
            }
            //唤醒主线程
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            Long ee = System.currentTimeMillis();
            System.out.println("内部循环时间为：" + (ee - ss) + "毫秒");
            deviceInstructionList.addAll(unitInstructionList);

        }
        Long e = System.currentTimeMillis();
        System.out.println("整个循环时间为：" + (e - s) + "毫秒");

        if (restoreList.size() > 0) {
            archivesRestoreService.saveBatch(restoreList);
        }

        if (CollectionUtils.isNotEmpty(deviceInstructionList)) {
            deviceInstructionService.batchInsert(deviceInstructionList);
        }

    }

    @Override
    public void checkContain(List<recoverDto> dtoList) {
        for (recoverDto dto : dtoList) {
            if (dto.getType().equals(AgreementConstants.DataType.PLOT)) {
                this.checkPlot(dto, dtoList);
            } else if (dto.getType().equals(AgreementConstants.DataType.BUILD)) {
                this.checkBuild(dto, dtoList);
            } else if (dto.getType().equals(AgreementConstants.DataType.BUILD_UNIT)) {
                this.checkBuildUnit(dto, dtoList);
            }
        }
    }

    /**
     * 数据为住户时，检查是否有包含该住户的数据
     *
     * @param d       当前数据
     * @param dtoList 数据集合
     */
    public void checkPlot(recoverDto d, List<recoverDto> dtoList) {
        dtoList = dtoList.stream().filter(vo ->
                        Integer.parseInt(vo.getType()) >= Integer.parseInt(AgreementConstants.DataType.PLOT)
                                && vo.getPlotId() != null && vo.getPlotId().equals(d.getPlotId()))
                .collect(Collectors.toList());
        if (dtoList.size() > 1) {
            throw new ServiceException(ERROR_MESSAGE);
        }
    }

    /**
     * 数据为楼栋时，检查是否有包含该楼栋的数据
     *
     * @param d       当前数据
     * @param dtoList 数据集合
     */
    public void checkBuild(recoverDto d, List<recoverDto> dtoList) {
        dtoList = dtoList.stream().filter(vo ->
                Integer.parseInt(vo.getType()) >= Integer.parseInt(AgreementConstants.DataType.BUILD)
                        && vo.getBuildId() != null && vo.getBuildId().equals(d.getBuildId())).collect(Collectors.toList());
        if (dtoList.size() > 1) {
            throw new ServiceException(ERROR_MESSAGE);
        }
    }

    /**
     * 数据为楼栋单元时，检查是否有包含该楼栋单元的数据
     *
     * @param d       当前数据
     * @param dtoList 数据集合
     */
    public void checkBuildUnit(recoverDto d, List<recoverDto> dtoList) {
        dtoList = dtoList.stream().filter(vo ->
                Integer.parseInt(vo.getType()) >= Integer.parseInt(AgreementConstants.DataType.BUILD_UNIT)
                        && vo.getBuildId() != null && vo.getBuildId().equals(d.getBuildId())
                        && vo.getUnitId() != null && vo.getUnitId().equals(d.getUnitId())).collect(Collectors.toList());
        if (dtoList.size() > 1) {
            throw new ServiceException(ERROR_MESSAGE);
        }
    }

}
